package com.asset.service.impl;

import com.asset.web.interceptor.LoginInterceptor;
import com.asset.mapper.AssetMapper;
import com.asset.mapper.CategoryMapper;
import com.asset.pojo.Asset;
import com.asset.pojo.Category;
import com.asset.pojo.User;
import com.asset.service.AssetService;
import com.asset.service.CommonService;
import com.asset.util.AssetExceptionEnum;
import com.asset.util.AssetManageException;
import com.asset.util.BaseAssetStatus;
import com.asset.util.PageResult;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import tk.mybatis.mapper.entity.Example;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * @author 11725
 */
@Service
public class AssetServiceImpl implements AssetService {

    @Resource
    private CommonService commonService;

    @Resource
    private CategoryMapper categoryMapper;

    @Resource
    private AssetMapper assetMapper;

    @Override
    public PageResult<Asset> selectByPage(Integer page, Integer rows, String name, Long cid, Long depaId) {
        PageHelper.startPage(page, rows);

        Example example = new Example(Asset.class);
        Example.Criteria criteria = example.createCriteria();
        if (!StringUtils.isBlank(name)) {
            criteria.andLike("name", "%" + name + "%");
        }
        if (cid != null) {
            criteria.andEqualTo("cid", cid);
        }
        if (depaId != null) {
            criteria.andEqualTo("depaId", depaId);
        }

        List<Asset> assetList = assetMapper.selectByExample(example);

        PageInfo<Asset> info = new PageInfo<>(assetList);

        return new PageResult<>(info.getTotal(), assetList);
    }

    @Override
    public Asset selectById(Long id) {
        Asset select = assetMapper.selectByPrimaryKey(id);
        if (select == null) {
            throw new AssetManageException(AssetExceptionEnum.ASSET_NOT_FOUND);
        }
        return select;
    }

    @Override
    public void insert(Asset asset) {
        // 参数校验
        check(asset);

        Category category = categoryMapper.selectByPrimaryKey(asset.getCid());

        // 设置折旧终止日期
        Date putDate = asset.getPutDate();
        Calendar completedDate = Calendar.getInstance();
        completedDate.setTime(putDate);
        completedDate.add(Calendar.YEAR, category.getYears());

        asset.setCompletedDate(completedDate.getTime());

        // 计算月折旧率
        BigDecimal total = asset.getPrice().multiply(new BigDecimal(asset.getNumber()));
        // 年折旧额 = (总价值 - 预计净残值)/折旧年限
        BigDecimal amountByYear = total.subtract(asset.getNetSalvage())
                .divide(new BigDecimal(category.getYears()), 3, BigDecimal.ROUND_HALF_UP);
        // 月折旧率 = (年折旧额/总价值)/12
        BigDecimal deprRate = amountByYear.divide(total, 3, BigDecimal.ROUND_HALF_UP)
                .divide(new BigDecimal("12"), 3, BigDecimal.ROUND_HALF_UP);
        asset.setDeprRate(deprRate);
        asset.setId(null);

        assetMapper.insertSelective(asset);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void update(Asset asset) {
        check(asset);
        // 字段处理
        Asset record = new Asset();
        record.setBrandId(asset.getBrandId());
        record.setCid(asset.getCid());
        record.setDepaId(asset.getDepaId());
        record.setDepoId(asset.getDepoId());
        record.setName(asset.getName());
        int row = assetMapper.updateByPrimaryKeySelective(record);
        if (row != 1) {
            throw new AssetManageException(AssetExceptionEnum.ASSET_UPDATE_ERROR);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void changeStatus(Long id, Integer to, Integer day) {
        Asset asset = assetMapper.selectByPrimaryKey(id);
        User user = LoginInterceptor.getUser();

        // 判断资产是否存在
        if (asset == null) {
            throw new AssetManageException(AssetExceptionEnum.ASSET_NOT_FOUND);
        }
        // 判断是否可操作
        if (!user.getDepaId().equals(asset.getDepaId())) {
            throw new AssetManageException(AssetExceptionEnum.ASSET_CHANGE_ERROR);
        }
        // 更新行数
        int row;

        if (to.equals(BaseAssetStatus.HAS_BEEN_BOOKED) || to.equals(BaseAssetStatus.WAIT_FOR_PROCESSING)) {
            // 预定 or 待处置
            row = this.changeStatusToReserveOrWait(asset, to, user.getId(), day);
        } else if (to.equals(BaseAssetStatus.AVAILABLE)) {
            // 返还闲置
            row = this.changeStatusToAvailable(asset);
        } else {
            // 其他
            row = this.changeStatusToOther(asset, to);
        }

        if (row != 1) {
            // 更新失败
            throw new AssetManageException(AssetExceptionEnum.ASSET_CHANGE_ERROR);
        }
    }

    @Override
    public List<Asset> selectByDepaIdAndStatus(Long depaId, Integer status) {
        Asset record = new Asset();
        record.setDepaId(depaId);
        record.setStatus(status);
        return assetMapper.select(record);
    }

    @Override
    public List<Asset> selectByUserId(Long userId) {
        Asset record = new Asset();
        record.setUserId(userId);
        return assetMapper.select(record);
    }

    @Override
    public PageResult<Asset> selectByDepreciation(Integer page, Integer rows) {
        PageHelper.startPage(page, rows);
        List<Asset> assetList = assetMapper.selectByDepreciation();

        PageInfo<Asset> info = new PageInfo<>(assetList);

        return new PageResult<>(info.getTotal(), assetList);
    }

    @Override
    public List<Map<String, String>> countByColumn(String column) {
        if ("category".equals(column)) {
            return assetMapper.countByCid();
        }
        if ("department".equals(column)) {
            return assetMapper.countByColumn("depa_id");
        }
        if ("status".equals(column)) {
            return assetMapper.countByColumn("status");
        }
        throw new AssetManageException(AssetExceptionEnum.COUNT_PARAM_ERROR);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void delete(Long id) {
        Asset record = new Asset();
        record.setId(id);
        int row = assetMapper.delete(record);
        if (row != 1) {
            throw new AssetManageException(AssetExceptionEnum.ASSET_NOT_FOUND);
        }
    }

    /**
     * 闲置 => 预定 or 待处置
     * @param select 原记录
     * @param userId 预定用户 id
     * @param day    天数
     */
    private int changeStatusToReserveOrWait(Asset select, int newStatus, Long userId, Integer day) {
        Asset record = new Asset();

        if (newStatus == BaseAssetStatus.HAS_BEEN_BOOKED) {
            // 资产预定
            record.setStatus(BaseAssetStatus.HAS_BEEN_BOOKED);
            record.setUserId(userId);

            // 设置到期时间
            Calendar instance = Calendar.getInstance();
            instance.add(Calendar.DAY_OF_MONTH, day);

            record.setDueDate(instance.getTime());
        } else if (newStatus == BaseAssetStatus.WAIT_FOR_PROCESSING) {
            // 待处置
            record.setStatus(BaseAssetStatus.WAIT_FOR_PROCESSING);
        } else {
            // 其他
            throw new AssetManageException(AssetExceptionEnum.ASSET_STATUS_IS_DISABLED);
        }

        Example example = new Example(Asset.class);

        // 资产更新条件：id、状态为闲置状态
        example.createCriteria()
                .andEqualTo("id", select.getId())
                .andEqualTo("status", BaseAssetStatus.AVAILABLE);

        // 预定
        return assetMapper.updateByExampleSelective(record, example);
    }

    /**
     * 待处置 => 维护 or 维修 or 转移 or 报废 or 变卖
     * @param select        原记录
     * @param newStatus     新的资产状态
     * @return              受影响行数
     */
    private int changeStatusToOther(Asset select, int newStatus) {
        boolean ok = newStatus == BaseAssetStatus.UNDER_MAINTENANCE
                || newStatus == BaseAssetStatus.UNDER_REPAIR
                || newStatus == BaseAssetStatus.BE_SCRAPPED
                || newStatus == BaseAssetStatus.BE_TRANSFERRED
                || newStatus == BaseAssetStatus.SOLD_OFF;
        if (ok) {
            Asset record = new Asset();
            record.setStatus(newStatus);

            Example example = new Example(Asset.class);
            example.createCriteria()
                    .andEqualTo("id", select.getId())
                    .andEqualTo("status", BaseAssetStatus.WAIT_FOR_PROCESSING);

            return assetMapper.updateByExampleSelective(record, example);
        }

        throw new AssetManageException(AssetExceptionEnum.ASSET_STATUS_IS_DISABLED);
    }

    /**
     * 预定 or 维护 or 维修 or 待处置 => 闲置
     * @param select 原记录
     * @return
     */
    private int changeStatusToAvailable(Asset select) {
        Asset record = new Asset();
        record.setUserId(0L);
        record.setStatus(BaseAssetStatus.AVAILABLE);

        boolean ok = select.getStatus().equals(BaseAssetStatus.HAS_BEEN_BOOKED)
                || select.getStatus().equals(BaseAssetStatus.UNDER_MAINTENANCE)
                || select.getStatus().equals(BaseAssetStatus.UNDER_REPAIR)
                || select.getStatus().equals(BaseAssetStatus.WAIT_FOR_PROCESSING);
        if (ok) {
            Example example = new Example(Asset.class);
            // 资产更新条件：id、资产状态
            example.createCriteria()
                    .andEqualTo("id", select.getId())
                    .andEqualTo("status", select.getStatus());

            return assetMapper.updateByExampleSelective(record, example);
        }
        // 资产状态不匹配
        throw new AssetManageException(AssetExceptionEnum.ASSET_STATUS_IS_DISABLED);
    }

    /**
     * 参数校验 - 校验 品牌/分类/部门/仓库 是否合法
     *
     * @param asset 参数对象
     */
    private void check(Asset asset) {
        if (!commonService.checkBrand(asset.getBrandId())) {
            // 品牌不存在
            throw new AssetManageException(AssetExceptionEnum.BRAND_NOT_FOUND);
        }
        if (!commonService.checkCategory(asset.getCid(), false)) {
            // 分类级别错误或不存在
            throw new AssetManageException(AssetExceptionEnum.CATEGORY_INVALID);
        }
        if (!commonService.checkDepartment(asset.getDepaId())) {
            // 部门不存在
            throw new AssetManageException(AssetExceptionEnum.DEPARTMENT_NOT_FOUND);
        }
        if (!commonService.checkDepository(asset.getDepoId(), asset.getDepaId())) {
            // 仓库与部门不匹配或不存在
            throw new AssetManageException(AssetExceptionEnum.DEPOSITORY_INVALID);
        }
    }
}
